/**********************************************************
* @ File name -> shell.cpp
* @ Version   -> V1.0
* @ Date      -> 20160912
* @ Brief     -> 串口控制台相关函数

 V1.*
* @ Revise    ->
**********************************************************/
#include <string.h>
#include <stdlib.h>
#include "shell.h"
#include "usart.h"
#include "platform.h"
#include "fsapi.h"
#include "rtc.h"
#include "mod_emeter.h"
#include "typedef.h"
#include "gtc_client.h"
#include "pwm.h"
#include "data.h"
#include "adc.h"
#include "mod_gun.h"
#include "mod_gui.h"
#include "mod_ftp_update.h"
#include "alarm.h"
#include "charger.h"

#define SHELL_PARA_LEN_MAX (70)
#define SHELL_PARA_NUM_MAX (5)
#define SHELL_RECV_BUF_MAX (100) //接收命令

/**/
struct T_ShellCmdPara {
    char buf[SHELL_PARA_LEN_MAX];
};

struct T_ShellCmd {
    struct T_ShellCmdPara para[SHELL_PARA_NUM_MAX];
    int size;
};

static char g_shell_recv_buf[SHELL_RECV_BUF_MAX];  /*串口队列中读取的数据*/
static char g_shell_recv_pos = 0;                  /*数据写入位置*/

static int32_t is_cmd(const char_t *src, const char_t *des){
    if (strcmp(src, des) == 0){
        return 1;
    }else{
        return 0;
    }
}

/*lint -e{715,830} */
static void CmdTask(const struct T_ShellCmd *list) {
#if 1
    uint8_t pcWriteBuffer[600] = {0};

    printf("=================================================\r\n");
    printf("free heap:%d\r\n\r\n", (int)xPortGetFreeHeapSize());
    printf("task name    status priority  stack  task num\r\n");
    vTaskList((char *)pcWriteBuffer);
    printf("%s\r\n", pcWriteBuffer);

    memset(pcWriteBuffer, 0, sizeof(pcWriteBuffer));
    
    //printf("\r\n任务名       运行计数       使用率\r\n");
    vTaskGetRunTimeStats((char *)&pcWriteBuffer);
    printf("%s\r\n", pcWriteBuffer);
#endif
}

static void set_date(const char *str){
    if (strlen(str) != 14){
        printf("time format err\r\n");
    }
    char year[5]={0};
    char mon[3]={0};
    char day[3]={0};
    char hour[3]={0};
    char min[3]={0};
    char sec[3]={0};
    sscanf(str, "%4c%2c%2c%2c%2c%2c", year, mon, day, hour, min, sec);
    rtc_set_time(strtol(year,NULL, 10),strtol(mon, NULL, 10), strtol(day, NULL, 10),
                 strtol(hour,NULL, 10),strtol(min,NULL, 10), strtol(sec,NULL, 10),1);        
}

static void cmd_time(const struct T_ShellCmd *list){
    if (list->size == 1) {
       char time[15]={0};
       rtc_get_currt_time_str(time, sizeof(time));
       printf("%s\r\n", time);
    }else if (list->size == 2){
        set_date(list->para[1].buf);
    }else{
        printf("cmd para num err\r\n");
    }
}

static void cmd_test(const struct T_ShellCmd *list){
    if(is_cmd(list->para[1].buf, "pwm")){
        if(is_cmd(list->para[2].buf, "on")){
            pwm_on();
        }else if(is_cmd(list->para[2].buf, "off")){
            pwm_off();
        }else{
            printf("para err\r\n");
        }
    }else if (is_cmd(list->para[1].buf, "cp")){
        if (is_cmd(list->para[2].buf, "yes")){
            printf("calibrating...\r\n");
            os_msleep(1000);
            printf("switch to 12v\r\n");
            os_msleep(3000);
            int32_t value = adc_get(ADC_CP);
            data_setint("cp12v", value, 10);
            printf("switch to 9v\r\n");
            os_msleep(3000);
            value = adc_get(ADC_CP);
            data_setint("cp9v", value, 10);
            printf("switch to 6v\r\n");
            os_msleep(3000);
            value = adc_get(ADC_CP);
            data_setint("cp6v", value, 10);
            printf("calibrate end");
        }
    }else if (is_cmd(list->para[1].buf, "charge")){
        if(is_cmd(list->para[2].buf, "on")){
            relay_charge_l_on();
            relay_charge_n_on();
        }else if(is_cmd(list->para[2].buf, "off")){
            relay_charge_l_off();
            relay_charge_n_off();
        }else{
            printf("para err\r\n");
        }    
    }else{
        printf("para err\r\n");
    }
    
    
    
}

static void cmd_at(const struct T_ShellCmd *list){
    if (list->size == 2){
        usart5_send((uint8_t *)list->para[1].buf, strlen(list->para[1].buf));
    }
}

void client_set_test_flag(uint8_t state);
static void cmd_debug(const struct T_ShellCmd *list){
    if (list->size == 3){
        if (is_cmd(list->para[1].buf, "gui")){
            gui_set_test_flag(strtol(list->para[2].buf, NULL, 10));
        }
        if (is_cmd(list->para[1].buf, "net")){
            client_set_test_flag(strtol(list->para[2].buf, NULL, 10));
        }
    }

}

//测试ftp
static void cmd_ftp(const struct T_ShellCmd *list){ 
    if (strlen(list->para[1].buf) != 0){
        ftp_get_file("106.54.177.37",21,"ftpuser","gtcxny123", "", (char_t *)list->para[1].buf); //MasterAPP.bin testftp.txt
    }
    else{
        printf("para err\n");
    }
}

#include "qrencode.h"

static void cmd_qrcode(const struct T_ShellCmd *list){
	 printf("encode test\r\n");
	
	  os_msleep(2000);		
		 QRcode *qrcode;
     qrcode = QRcode_encodeString("http://weixin.qq.com/r/bSqZgffE-qqBrbPi939-",0, QR_ECLEVEL_H, QR_MODE_8,1);
     int x;
     int y;
		 printf("qrcode->width %d\r\n",qrcode->width);
				
     for(y=0;y<qrcode->width;y++) {
     for(x=0;x<qrcode->width;x++){
        if(qrcode->data[y*qrcode->width+x]&0x01){
          printf("■");
        }else{
          printf(" ");
        }
    }
     printf("\r\n");
    }
    printf("\r\n");
		
    QRcode_free(qrcode);
}
static void CmdSetConfig(const struct T_ShellCmd *list) {
    if (list->size == 1) {
        data_show_config();
        return;
    }

    if ((strlen(list->para[1].buf) > 0) && (strlen(list->para[2].buf) > 0)){
        if (data_is_key_exist(list->para[1].buf)){
            data_setstr(list->para[1].buf, list->para[2].buf);
        }else{
            printf("set key err\r\n");
        }
    }else{
        printf("set num err\r\n");
    }
}

static void CmdSetSubId(const struct T_ShellCmd *list) {
    #if 0
    if (list->size == 1) {
        GtcData_ShowSubDevId();
    } else if (list->size == 2) {
        uint8_t num = (uint8_t)atoi(list->para[1].buf);
        if ((num > 0) && (num <= GTC_DEV_NUM_MAX)) {
            printf("delete subid num:%d\r\n", num);
            GtcData_SetSubDevId(num - 1, 0);
            GtcData_SaveToFile();
        }
    } else if (list->size == 3) {
        uint8_t num = (uint8_t)atoi(list->para[1].buf);
        unsigned int subid = GetSubDevIdValue((const char *)list->para[2].buf);
        if ((num > 0) && (num <= GTC_DEV_NUM_MAX)) {
            GtcData_SetSubDevId(num - 1, subid);
            GtcData_SaveToFile();
        }
    }
    #endif
}

static void CmdReboot(const struct T_ShellCmd *list) {
    if (strcmp(list->para[1].buf, "yes") == 0) {
        os_reboot();
    }
}

static void CmdShow(const struct T_ShellCmd *list) {
    if (strcmp(list->para[1].buf, "emeter") == 0) {
        if (strlen(list->para[2].buf) != 0){
            uint32_t vol = emeter_getvalue(strtol(list->para[2].buf, NULL, 10), EM_TYPE_VOL);
            uint32_t currt = emeter_getvalue(strtol(list->para[2].buf, NULL, 10), EM_TYPE_CURRT);
            uint32_t kwh = emeter_getvalue(strtol(list->para[2].buf, NULL, 10), EM_TYPE_KWH);
            printf("emeter id:%s vol:%d currt:%d kwh:%d\r\n" ,list->para[2].buf, vol, currt, kwh);
        }else{
            printf("please keyin 1(left) 2(right) emeter num\r\n");
        }
    }
    if (strcmp(list->para[1].buf, "net") == 0) {
        printf("net state:%d relogin:%d 4gcsq:%d\r\n", client_get_net_login_state(), client_get_relogin_cnt(), 0);
    }
    if (strcmp(list->para[1].buf, "gun") == 0){
        printf("gunstate:%d cpvalue:%d\r\n", gun_get_state(1), gun_get_cp_value());
    }
    if (strcmp(list->para[1].buf, "alarm") == 0){
        char_t err1[5]={0};
        char_t err2[5]={0};
        alarm_get_alarmcode(1, err1);
        alarm_get_alarmcode(2, err2);
        char_t alarm1[50]={0};
        char_t alarm2[50]={0};
        alarm_get_alarm_desc(1, alarm1);
        alarm_get_alarm_desc(2, alarm2);
        printf("gun1 alarm code:%s %s gun2 alarm code:%s %s\r\n", err1, alarm1, err2, alarm2);
    }
    if (strcmp(list->para[1].buf, "sys") == 0){
        charger_print();
    }
    
    

    #if 0
    if (strcmp(list->para[1].buf, "state") == 0) {
        T_GtcMsgArray msg = T_GtcMsgArrayInitaller;
        msg.msgId = MSG_SHELL;
        msg.dataH = MSG_SHELL_SHOW_DEV_STATE;
        Msg_SendMsgToDev(&msg);
    }
    if (strcmp(list->para[1].buf, "time") == 0) {
        ShowCurrentTime();
    }
    if (strcmp(list->para[1].buf, "sys") == 0) {
        ShowSysMode();
    }
    if (strcmp(list->para[1].buf, "ver") == 0) {
        ShowSysVer();
    }
    #endif
}

static void cmd_ls(const struct T_ShellCmd *list){
    if (list->size == 1){
        fs_ls(".");
    }else{
        fs_ls(list->para[1].buf);
    }
}

static void cmd_rm(const struct T_ShellCmd *list){
    if (strlen(list->para[1].buf) != 0) {
        fs_delete(list->para[1].buf);
    }    
}

static void cmd_cat(const struct T_ShellCmd *list){
    
    if (strlen(list->para[1].buf) != 0) {
        fs_cat(list->para[1].buf);
    }
}

static void cmd_format(const struct T_ShellCmd *list){
    if(strcmp(list->para[1].buf, "yes") == 0){
        fs_format();
        printf("format disk success\r\n");
    }
}

/***************以上是串口命令实现函数**********************/

static void Shell_Cmd_SetPara(struct T_ShellCmdPara *para, const char *val) {
    int len = (strlen(val) >= SHELL_PARA_LEN_MAX) ? (SHELL_PARA_LEN_MAX - 1) : strlen(val);
    memcpy(para->buf, val, len);
    para->buf[len] = 0;
}

static void Shell_Cmd_PushBack(struct T_ShellCmd *list, const struct T_ShellCmdPara *para) {
    if (list->size >= SHELL_PARA_NUM_MAX) {
        return;
    }
    int len = (strlen(para->buf) >= SHELL_PARA_LEN_MAX) ? (SHELL_PARA_LEN_MAX - 1) : strlen(para->buf);
    memcpy(list->para[list->size].buf, para->buf, len);
    list->para[list->size].buf[len] = 0;
    list->size++;

}

static int Shell_Cmd_Size(const struct T_ShellCmd *list) {
    return list->size;
}

static struct T_ShellCmdPara *Shell_Cmd_At(struct T_ShellCmd *list, int pos) {
    if (pos >= list->size) {
        return NULL;
    }
    return &list->para[pos];
}

struct T_ShellInfo {
    char *cmd;
    void (* DealFunc)(const struct T_ShellCmd *list);
    char *desc;
};


/*lint -e{715,830} */
static void Shell_CmdHelp(const struct T_ShellCmd *list) {
    printf("***************************************\r\n");
    printf("task     show freertos task info\r\n");
    printf("set      [sip/sport/lip/mask/gw/mac/devid/stopcurrent]\r\n");
    printf("reboot   [yes] \r\n");
    printf("show     [state/time] \r\n");
    printf("disk     [ls/rm/cat] \r\n");
    printf("***************************************\r\n");
}

static void Shell_Cmd(const struct T_ShellCmd *list);

static const struct T_ShellInfo shellInfo[] = {
    {"?",          Shell_Cmd,    	    "show all cmd"},
    {"help",       Shell_CmdHelp,     	"show all cmd usage"},
    {"task",       CmdTask,     		"show all task info"},
    //{"control",    CmdControl,          "control the hardwaer"},
    //{"fileopera",  CmdFileOpera,        "show filemsg"},
    {"set",        CmdSetConfig,        "SetConfig para"},
    {"subid",      CmdSetSubId,         "Set sub dev id"},
    {"reboot",     CmdReboot,			"reboot sys"},
    {"show",       CmdShow,             "show info"},
    {"time",       cmd_time,            "show or set time"},
    {"ls",         cmd_ls,              "show dir"},
    {"cat",        cmd_cat,             "show file"},
    {"rm",         cmd_rm,              "rm file" },
    {"format",     cmd_format,          "format disk"},
    {"test",       cmd_test,            "test board"},
    {"at",         cmd_at,              "tast at"},
    {"debug",      cmd_debug,           "set debug flag"},
    {"ftp",        cmd_ftp,             "ftp test"},
		{"qrcode",     cmd_qrcode,          "encode test"},
    {"", NULL, ""}
};

/*lint -e{715,830} */
static void Shell_Cmd(const struct T_ShellCmd *list) {
    printf("*********************************************\r\n");
    printf("num\tcmd\t\tdesc\r\n");
    printf("*********************************************\r\n");
    int i = 0;
    while(shellInfo[i].DealFunc != 0) {
        printf("%5d\t%-10s\t\t%s\r\n", i + 1, shellInfo[i].cmd, shellInfo[i].desc);
        i++;
    }
    printf("*********************************************\r\n");
}

static void Shell_GetCmd(const char *cmdStr, struct T_ShellCmd *list) {
    char buf[SHELL_RECV_BUF_MAX] = {0};
    memcpy(buf, cmdStr, SHELL_RECV_BUF_MAX);

    char *ptr = strtok(buf, " ");
    while(ptr != NULL) {
        struct T_ShellCmdPara para;
        Shell_Cmd_SetPara(&para, ptr);
        Shell_Cmd_PushBack(list, &para);

        ptr = strtok(NULL, " ");
    }
}

static void Shell_Deal(struct T_ShellCmd *list) {
    struct T_ShellCmdPara *para = Shell_Cmd_At(list, 0);
    if (para == NULL) {
        printf("cmd empty!\r\n");
        return;
    }

    uint8_t i = 0;
    while(shellInfo[i].DealFunc != 0) {
        if (memcmp(shellInfo[i].cmd, para->buf, strlen(para->buf)) == 0) {
            shellInfo[i].DealFunc(list);
            return;
        }
        i++;
    }
    printf("cmd \"%s\" err!\r\n", Shell_Cmd_At(list, 0)->buf);
}

static void shell_parse(char *data){
    struct T_ShellCmd cmd_list;
    memset(&cmd_list, 0, sizeof(cmd_list));     
    Shell_GetCmd(data, &cmd_list);
    if (Shell_Cmd_Size(&cmd_list) != 0) {
        printf("\r\n");
        Shell_Deal(&cmd_list);
        printf("\r\n");
    }
}

void shell_recv_poll(void){
    uint8_t val;
    if (usart1_receive(&val)){
        if ((val == '\n') || (val == '\r')) {
            if (g_shell_recv_pos == 0){
                //printf("1\r\n");
                return;
            }
            g_shell_recv_buf[g_shell_recv_pos]=0;
            shell_parse(g_shell_recv_buf);
            g_shell_recv_pos = 0;
            return;
        }

        if (g_shell_recv_pos >= SHELL_RECV_BUF_MAX) {
            g_shell_recv_pos = 0;
            return;
        }
        if (val == '\b') {
            if (g_shell_recv_pos != 0){
                g_shell_recv_pos--;
            }
            return;
        }
        g_shell_recv_buf[g_shell_recv_pos++] = val;     
    }
}

void shell_task(void *pvParameters) {

    /*lint -e(716) */
    while(1) {
        shell_recv_poll();
        os_msleep(10);
    }
}

int32_t shell_init(void){
    int32_t ret = 0;
    os_pthread_t shell_thread;
    if(os_pthread_create("shell", &shell_thread, shell_task, NULL, OS_TASK_PRIO_LOW,  OS_STACK_SIZE_LARGE*2) != 0){
        ret = -1;
    }
    return ret;
}

